package hust.cs.javacourse.search.query.impl;

import hust.cs.javacourse.search.index.AbstractTerm;
import hust.cs.javacourse.search.index.impl.Index;
import hust.cs.javacourse.search.index.impl.Posting;
import hust.cs.javacourse.search.index.impl.PostingList;
import hust.cs.javacourse.search.query.AbstractHit;
import hust.cs.javacourse.search.query.AbstractIndexSearcher;
import hust.cs.javacourse.search.query.Sort;
import hust.cs.javacourse.search.util.FileUtil;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.*;

public class IndexSearcher extends AbstractIndexSearcher {
    @Override
    public void open(String indexFile) {
        try{
            ObjectInputStream in = new ObjectInputStream(new FileInputStream(indexFile));
            //注意这个时候不是调用构造函数，而是直接从文件反序列化到内存里产生对象
            this.index.readObject(in);
            System.out.println(this.index);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 根据单个检索词进行搜索
     * @param queryTerm ：检索词
     * @param sorter ：排序器
     * @return
     */
    @Override
    public AbstractHit[] search(AbstractTerm queryTerm, Sort sorter) {
        //获取命中的PostingList
        PostingList postingList = (PostingList) (this.index.search(queryTerm));
        Posting posting;
        if(postingList == null)
            return new Hit[0];

        int size = postingList.size();               //获取PostingList的大小

        AbstractHit[] hit = new Hit[size];           //创建命中对象
        //遍历Posting并进行排序
        for(int i = 0;i < size;i++) {
            posting = (Posting) (postingList.get(i));
            hit[i] = new Hit(posting.getDocId(),                    //设置命中文档的ID和路径
                    this.index.getDocName(posting.getDocId()));
            hit[i].setContent(FileUtil.read(this.index.getDocName(posting.getDocId())));
            hit[i].setScore(-posting.getFreq());                  //设置命中文档的分数
            hit[i].termPostingMapping.put(queryTerm,posting);                         //设置命中文档内容
            //System.out.println(queryTerm.toString());
        }
        List<AbstractHit> hitList = Arrays.asList(hit);             //Hit数组转List
        sorter.sort(hitList);                                       //对List排序
        //hit = (Hit[]) (hitList.toArray());                          //List转为数组
        for(int i = 0;i < size;i++)
            hit[i] = hitList.get(i);
        return hit;
    }

    @Override
    public AbstractHit[] search(AbstractTerm queryTerm1, AbstractTerm queryTerm2, Sort sorter, LogicalCombination combine) {
        List<AbstractHit> hitRepeat = new ArrayList<>();
        AbstractHit[] hit1 = this.search(queryTerm1, sorter);
        AbstractHit[] hit2 = this.search(queryTerm2, sorter);
        if(combine == LogicalCombination.OR) {
            for(int i = 0;i < hit1.length;i++) {
                hitRepeat.add(hit1[i]);
            }
            for(int i = 0;i < hit2.length;i++) {
                if(hitRepeat.contains(hit2[i])) {
                    int indexHit = hitRepeat.indexOf(hit2[i]);          //找到重复的hit
                    //System.out.println("hit2: " + hitRepeat.get(indexHit).getScore());
                    //System.out.println("hit2: " + hit2[i].getScore());
                    //重新计算分数
                    hitRepeat.get(indexHit).setScore(hit2[i].getScore() + hitRepeat.get(indexHit).getScore());
                    Iterator it = hit2[i].termPostingMapping.entrySet().iterator();
                    while(it.hasNext()) {
                        Map.Entry entry = (Map.Entry) it.next();
                        AbstractTerm key = (AbstractTerm) entry.getKey();
                        hitRepeat.get(indexHit).termPostingMapping.put(key,hit2[i].termPostingMapping.get(key));
                    }
                    continue;
                }

                hitRepeat.add(hit2[i]);
            }
            sorter.sort(hitRepeat);
            AbstractHit[] hit = new AbstractHit[hitRepeat.size()];
            for(int i = 0;i < hitRepeat.size();i++) {
                hit[i] = hitRepeat.get(i);
            }
            return hit;
        } else {
            List<AbstractHit> hitList = new ArrayList<>();
            List<AbstractHit> hitList1 = Arrays.asList(hit1);
            List<AbstractHit> hitList2 = Arrays.asList(hit2);

            if(hit1.length < hit2.length) {
                for(int i = 0;i < hit1.length;i++) {
                    if(hitList2.contains(hit1[i])) {
                        int index = hitList2.indexOf(hit1[i]);
                        hit1[i].setScore(hit1[i].getScore() + hitList2.get(index).getScore());
                        hitList.add(hit1[i]);
                        int listNum = hitList.indexOf(hit1[i]);

                        Iterator it = hit2[index].termPostingMapping.entrySet().iterator();
                        while(it.hasNext()) {
                            Map.Entry entry = (Map.Entry) it.next();
                            AbstractTerm key = (AbstractTerm) entry.getKey();
                            hitList.get(listNum).termPostingMapping.put(key,hit2[index].termPostingMapping.get(key));
                        }
                    }
                    else
                        continue;
                }
            } else {
                for(int i = 0;i < hit2.length;i++) {
                    if(hitList1.contains(hit2[i])) {
                        int index = hitList1.indexOf(hit2[i]);
                        hit2[i].setScore(hit2[i].getScore() + hitList1.get(index).getScore());
                        hitList.add(hit2[i]);

                        int listNum = hitList.indexOf(hit2[i]);

                        Iterator it = hit1[index].termPostingMapping.entrySet().iterator();
                        while(it.hasNext()) {
                            Map.Entry entry = (Map.Entry) it.next();
                            AbstractTerm key = (AbstractTerm) entry.getKey();
                            hitList.get(listNum).termPostingMapping.put(key,hit1[index].termPostingMapping.get(key));
                        }
                    }
                    else
                        continue;
                }
            }
            sorter.sort(hitList);
            AbstractHit[] hit = new AbstractHit[hitList.size()];
            for(int i = 0;i < hitList.size();i++)
                hit[i] = hitList.get(hitList.size()-i-1);
            return hit;
        }
    }
}
